<?php

include_once './db/SignFieldDB.php';

/*
# 报警条目 表结构
CREATE TABLE `t_alarm` (
`_id` int(11) unsigned PRIMARY KEY AUTO_INCREMENT COMMENT '主键',
`alarm_number` int(11) DEFAULT 0 COMMENT '异常次数,相同的异常都会加1累计',
`summary` varchar(250) COMMENT '概要',
`content` text COMMENT '报警内容',
`ruleId` varchar(250) COMMENT '规则id',
`mark` varchar(100) comment '异常信息的标识(统一对报警内容和报警支付类型与报警级别信息加密做相同类型异常只记录一条，当到达一定数量时再真正执行发送，后续相同类型异常累计次数)',
`touch_start_time` int(11) DEFAULT 0 COMMENT '记录异常的开始时间',
`touch_end_time` int(11) DEFAULT 0 COMMENT '异常的最后(最近)记录时间',
`touch_path` varchar(100) DEFAULT '' COMMENT '异常时所执行的所有关联文件,及程序',
`exception` varchar(500) DEFAULT '' COMMENT '异常内容',
`request_source` varchar(100) DEFAULT '' COMMENT '请求来源(可记录当前请求的访问地址url)',
`request_data` text COMMENT '请求数据',
`is_alarm` char(10) DEFAULT 'no' COMMENT '是否已发送报警：no.未发',
`alarm_date` datetime DEFAULT null COMMENT '发送时间',
`created_time` datetime DEFAULT null COMMENT '创建时间',
index `number`(`alarm_number`),
index `mark`(`mark`),
index `touch_end_time`(`touch_end_time`),
index `base` (`mark`,`created_time`,`is_alarm`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8;
*/  

class AlarmDB
{
    /**
     * @var \PDO $pdo
     */
    protected $pdo = null;
    protected static $instance = null;
    protected $table_name = "t_alarm";

    protected $config = [
        "host" => "127.0.0.1",
        "db_name" => "fancyalarm",
        "user_name" => "root",
        "password" => "root",
    ];

    /**
     * 创建db连接
     *
     *
     * @return AlarmDB|null
     */
    public static function connect()
    {
        if (self::$instance == null) {
            self::$instance = new self();
        }
        return self::$instance;
    }


    /**
     * 获取实例
     *
     * @return PDO
     * @throws AlarmException
     */
    protected function db()
    {
        try {
            if (empty($this->pdo)) {
                $host = $this->config['host'];
                $db_name = $this->config['db_name'];
                $this->pdo = new PDO("mysql:host={$host};dbname={$db_name};charset=utf8", $this->config['user_name'], $this->config['password']);
            }
        } catch (\PDOException $e) {
            throw new AlarmException("数据库连接失败:" . $e->getMessage(), $e->getCode());
        }
        return $this->pdo;
    }


    /**
     * 执行sql
     *
     *
     * @param string $sql
     * @return array|bool
     * @throws AlarmException
     */
    public function query($sql)
    {
        $is_action = false;
        if (strpos($sql, "insert") !== false || strpos($sql, "update") !== false
            || strpos($sql, "create") !== false || strpos($sql, "drop") !== false || strpos($sql, "alter") !== false) {
            $is_action = true;
        }
        try {
            $db = $this->db();
            if ($is_action) {
                $result = $db->prepare($sql);
                return $result->execute();//返回受影响的记录条数,num为int类型;
            }
            $stmt = $db->query($sql);
            $rows = [];
            while ($row = $stmt->fetch(PDO::FETCH_ASSOC)) {
                $rows[] = $row;
            }
            return $rows;
        } catch (\PDOException $e) {
            throw new AlarmException($e->getMessage());
        }
    }

    /**
     * 查询集合
     *
     *
     * @param $sql
     * @return array|bool
     * @throws AlarmException
     */
    public function select($sql)
    {
        return $this->query($sql);
    }
    
    /**
     * 查询一个
     *
     *
     * @param string $sql
     * @return array|mixed
     * @throws AlarmException
     */
    public function first($sql)
    {
        $result = $this->select($sql);
        return empty($result) ? [] : array_shift($result);
    }

    /**
     * 添加操作
     *
     *
     * @param array $data
     * @return bool
     * @throws AlarmException
     */
    public function add($data)
    {

        try {
            if (!is_array($data)) {
                return false;
            }
            $data = $this->filterData($data);
            if (empty($data)) {
                return false;
            }
            $field = $this->bindField($data);

            $value = $this->bindValue($data);
            $sql = "insert into {$this->table_name} set %s";


            $result = $this->db()->prepare(vsprintf($sql, implode(",", $field)));
            return $result->execute($value);//返回受影响的记录条数,num为int类型;
        } catch (\PDOException $e) {
            throw new AlarmException($e->getMessage());
        }
    }


    /**
     * 更新操作
     *
     *
     * @param $data
     * @param $where
     * @return bool
     * @throws AlarmException
     */
    public function update($data, $where)
    {
        try {
            if (empty($data)) {
                return false;
            }
            if (!is_array($data)) {
                return false;
            }
            $data = $this->filterData($data);

            $field = $this->bindField($data);

            $value = $this->bindValue($data);

            $sql = "update {$this->table_name} set %s where 1 = 1 and " . $where;
            // dd(vsprintf($sql, implode(",", $field)));
            $result = $this->db()->prepare(vsprintf($sql, implode(",", $field)));

            return $result->execute($value);//返回受影响的记录条数,num为int类型;
        } catch (\PDOException $e) {
            throw new AlarmException($e->getMessage());
        }
    }

    /**
     * 过滤空值
     *
     *
     * @param $data
     * @return array
     */
    protected function filterData($data)
    {
        return array_filter($data, function ($v) {
            return !empty($v);
        });
    }


    /**
     * 绑定字段
     *
     *
     * @param $data
     * @return array
     */
    protected function bindField($data)
    {
        $field = array_map(function ($v) {
            return $v . "=:" . $v;
        }, array_keys($data));
        return $field;
    }

    /**
     * 绑定值
     *
     *
     * @param $data
     * @return array
     */
    protected function bindValue($data)
    {
        $value = [];
        array_walk($data, function ($v, $k) use (&$value) {
            $value[":" . $k] = $v;
        });
        return $value;
    }

    /**
     * 关闭连接
     *
     *
     */
    public function close()
    {
        $this->pdo = null;
    }
}


class Alarm{

    /**
     * 报警概要(类似于标题)
     * @var $summary
     */
    protected $summary = '';

    /**
     * 报警内容
     * @var $content
     */
    protected $content = '';

    /**
     * 操作时间
     * @var $target_time
     */
    protected $target_time = 0;

    
    /**
     * 请求数据
     * @var $request_data
     */
    protected $request_data = [];

    /**
     * 程序追溯记录
     * @var AlarmDB $db
     */
    protected $db = null;

    public function __construct()
    {
        $this->_init();
        $this->cookie = new Cookie();
    }

    /**
     *
     *
     * @return void
     */
    protected function _init()
    {
        $this->target_time = time();
       $this->request_data = $_REQUEST; //请求数据
    }

    /**
     * 异常记录
     *
     *
     * @param $exception
     */
    public function recordException($exception)
    {

    }

    /**
     * 生成标示码
     *
     * 标识码必须统一规则
     *
     * @param $summary
     * @param $ruleId
     * @param $fromIp
     * @param $toIp
     * @return string
     */
    protected function makeMark($summary, $ruleId, $fromIp)
    {
        return md5(base64_encode(serialize($summary) . $ruleId . $fromIp));
    }

     /**
     * 存储一条报警数据
     * 
     * @param string $summary 概要
     * @param string $content
     * @param string $ruleId 规则id
     * @return array|bool
     */
    public function record($summary, $content, $ruleId)
    {

        $result = false;
        try {
            do {
                $this->summary = $summary;
                $this->content = $content;
                $this->ruleId = $ruleId;

                // 标示是否发送通知
                $is_send = false;
               
                $mark = $this->makeMark($this->summary, $this->ruleId, 'fromIp');
                $this->db = AlarmDB::connect();
                $result = $this->store($summary, $content, $ruleId, $mark, $is_send);
                break;
            } while (null);
        } catch (AlarmException $exception) {
            $result = false;
        } catch (Exception $exception) {
            $result = false;
        }

        // 关闭数据库连接实例
        if ($this->db instanceof AlarmDB) {
            $this->db->close();
        }

        return $result;
    }

    public function getList()
    {
        $SignField = new SignField();
        $last_index = $SignField->getVal("lastIndex");
        $last_index =  empty($last_index) ? 0 : $last_index;
        
        // 最后一次操作的自增id
        $this->db = AlarmDB::connect();
        $maxId = 0;
        try {
            $max = $this->db->first("select max(_id) as max_id from t_alarm where is_alarm = 'no'");
            if(!empty($max)) $maxId = $max['max_id'];
            $res = $this->db->query("select * from t_alarm where 
            _id between {$last_index} and {$maxId} and is_alarm = 'no' order by _id desc");
            // var_dump($res);
            // echo "<br/>";
            $last_index = $maxId + 1;
            $updateRes = $SignField->update("lastIndex", $last_index);
            if(!$updateRes) return false;
            return $res;
        } catch (AlarmException $e) {
            return false;
        }
    }

    /**
     * 保存报警记录
     *
     * @param $summary
     * @param $content
     * @param $ruleId
     * @param $mark
     * @param bool $is_send
     * @return bool
     */
    protected function store($summary, $content, $ruleId, $mark, $is_send = false)
    {
        try {
            $res = $this->db->add([
                "ruleId" => $ruleId,
                "alarm_number" => 1,
                "summary" => $summary,
                "content" => $content,
                "mark" => $mark,
                "touch_start_time" => $this->target_time,
                "touch_end_time" => $this->target_time,
                "touch_path" => "",
                "exception" => "",
                "request_source" => "",
                "request_data" => $this->getRequestData(),
                "is_alarm" => $is_send ? "yes" : "no",
                "alarm_date" => $is_send ? date("Y-m-d H:i:s", $this->target_time) : "",
                "created_time" => date("Y-m-d H:i:s", $this->target_time),
            ]);
            if ($res === false) {
                return false;
            }
        } catch (AlarmException $e) {
            return false;
        }
        return true;
    }

    /**
     * 更新状态为：已经报警过
     * @param array $arr_id
     * @return bool
     */
    public function upDateIsAlarm($arr_id)
    {
        $char = implode(",", $arr_id);
        try {
            $this->db = AlarmDB::connect();
            $res = $this->db->update(["is_alarm" => "yes"],"_id in({$char})");
            if ($res === false) {
                return false;
            }
        } catch (SignFieldException $e) {
            return false;
        }
        return true;
    }

    /**
     * api接口：存一条记录
     * 
     */
    public function api_addRecord()
    {
        $summary = $_GET['summary'];
        $content = $_GET['content'];
        $ruleId = $_GET['ruleId'];
        $data = array(
            'result' => 1,
            'msg' => '添加失败',
            'data' => null
            );
        $res = self::record($summary,$content,$ruleId);
        if($res)
            $data = array(
                'result' => 0,
                'msg' => '添加成功',
                'data' => null
                );
        echo json_encode($data, JSON_UNESCAPED_UNICODE);
    }
    

    /**
     * 获取程序执行栈
     *
     * @param $index
     * @return array
     */
    protected function getRequestData()
    {
        return json_encode($this->request_data);
    }
}


class AlarmException extends Exception
{

}
